Cairo L2<->L1
最も一般的なdeposite-withdrawの流れを例にL2<->L1のやりとりを見ていく
https://www.youtube.com/watch?v=QloxAlUN_fc
L2->L1
https://scrapbox.io/files/6239f5be98fc5b0021028cac.png
1. send_message_to_l1()という関数が用意されており、引数として(to,payload)を有する
to:送信先L1 contractのアドレス
payload:送信するデータ
2. L1側でL2 Tx を含むstateが更新されると、L1 のStarkNet core contractにてそのメッセージがstoreされる
3. toアドレスで指定されたL1contractは、StarkNet core contractのconsumeMessageFromL2()を呼び出す
fromは自動的に追加される?
code:withdraw
@external
func withdraw{syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_check_ptr}(
user : felt, amount : felt):
# Make sure 'amount' is positive.
assert_nn(amount)
let (res) = balance.read(user=user)
tempvar new_balance = res - amount
# Make sure the new balance will be positive.
assert_nn(new_balance)
# Update the new balance.
balance.write(user, new_balance)
# Send the withdrawal message.
let (message_payload : felt*) = alloc()
assert message_payload0 = MESSAGE_WITHDRAW assert message_payload1 = user assert message_payload2 = amount send_message_to_l1(to_address=L1_CONTRACT_ADDRESS, payload_size=3, payload=message_payload)
return ()
end
新しい暗黙の引数 syscall_ptrにより、send message ()を含むStarkNet OSのいくつかの関数を呼び出すことができる
L2<-L1
1. send_message()関数がStarkNet core contractに用意されており、ここでメッセージをstoreしている。
この場合、messageにはselectorという追加フィールドがあり、対応するL2 contractの中でどの関数を呼び出すかを決定する。
2. StarkNet Sequencerは自動的にmessageを受信し、"to "アドレスで指定されたcontractの要求されたL2関数を呼び出す
Sequencerはバリデータみたいに役割の一つ?
誠実なSequencerは自動的に L1 -> L2 メッセージを消費するが、それはプロトコルによって強制されるものではない
したがってSequencerはmessageをスキップでき、このことは2つのcontract間のやりとりを設計する際に考慮されるべき点
code:deposite
%lang starknet
from starkware.cairo.common.alloc import alloc
from starkware.cairo.common.cairo_builtins import HashBuiltin
from starkware.cairo.common.math import assert_nn
from starkware.starknet.common.messages import send_message_to_l1
const L1_CONTRACT_ADDRESS = (
0x2Db8c2615db39a5eD8750B87aC8F217485BE11EC)
const MESSAGE_WITHDRAW = 0
# A mapping from a user (L1 Ethereum address) to their balance.
@storage_var
func balance(user : felt) -> (res : felt):
end
@view
func get_balance{syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_check_ptr}(
user : felt) -> (balance : felt):
let (res) = balance.read(user=user)
return (res)
end
@external
func increase_balance{syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_check_ptr}(
user : felt, amount : felt):
let (res) = balance.read(user=user)
balance.write(user, res + amount)
return ()
end
@l1_handler
func deposit{syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_check_ptr}(
from_address : felt, user : felt, amount : felt):
# Make sure the message was sent by the intended L1 contract.
assert from_address = L1_CONTRACT_ADDRESS
# Read the current balance.
let (res) = balance.read(user=user)
# Compute and update the new balance.
tempvar new_balance = res + amount
balance.write(user, new_balance)
return ()
end
L1 contractから送信されたメッセージを処理するためにL1 handlerってやつが呼び出される
StarkNet contractは、いくつかのL1 handlerを定義することができ、それらはselectorと呼ばれる整数値で識別される
L1contractがmessageを送信したい場合、StarkNet Core contractのsendMessageToL2()を呼び出し、L2 contractのアドレスと呼び出されるhandlerのselectorを指定する
selector - L1 handler